﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using parser=KeyWordSearch.TreeParser;
using KeyWordSearch;

namespace KeyWordSearch.ProcessTree
{
	internal static class ProcessTreeBuilder
	{
		public static RootNode BuildTree(parser::AstNode astRoot)
		{
			RootNode ProcessRoot = new RootNode();

			if (astRoot.Value.Attribute != parser::TokenType.Root)
			{
				throw new ProcessInconsistencyException("引数指定されたASTノードがRootではありません");
			}

			BuildTree(astRoot, ProcessRoot);

			return ProcessRoot;
		}

		private static void BuildTree(parser::AstNode astRoot, NonTerminalNode processRoot)
		{
			NonTerminalNode node;

			for (int i = 0; i < astRoot.Count; i++)
			{
				switch (astRoot[i].Value.Attribute)
				{
					case parser::TokenType.OperatorShortCutedAND:
						node = new OperatorShortCuttedAndProcess(processRoot);
						processRoot.Childes.Add(node);
						BuildTree(astRoot[i], node);
						break;

					case parser::TokenType.OperatorShortCutedOR:
						node = new OperatorShortCuttedOrProcess(processRoot);
						processRoot.Childes.Add(node);
						BuildTree(astRoot[i], node);
						break;

					case parser::TokenType.OperatorNOT:
						node = new OperatorNotProcess(processRoot);
						processRoot.Childes.Add(node);
						BuildTree(astRoot[i], node);
						break;

					case parser::TokenType.Identifyier:
						BuildTerminal(astRoot[i], processRoot);
						break;

					default:
						throw new ProcessInconsistencyException("想定外のTokenです");
				}
			}

		}

		private static void BuildTerminal(parser::AstNode astRoot, NonTerminalNode processRoot)
		{
			//Verify section
			if (astRoot.Value.TextValue.ToLower() != "contain")
			{
				throw new RecognizationException(string.Format("{0}は認識できない識別子です", astRoot.Value.TextValue), astRoot.Value);
			}

			if (astRoot.Count != 3)
			{
				throw new RecognizationException("引数の数が異なっています", astRoot.Value);
			}

			if (astRoot[0].Value.Attribute != parser::TokenType.StringLiteral)
			{
				throw new RecognizationException("第1引数が文字列以外です", astRoot[0].Value);
			}

			if (!(astRoot[1].Value.Attribute == parser::TokenType.Identifyier 
				|| astRoot[1].Value.Attribute == parser::TokenType.OperatorOR))
			{
				throw new RecognizationException("第２引数が不正です", astRoot[1].Value);
			}

			if (astRoot[2].Value.Attribute != parser::TokenType.Identifyier)
			{
				throw new RecognizationException("第３引数が不正です", astRoot[2].Value);
			}

			string CP = BuildCP(astRoot[0]);

			InspectionField Fld = BuildFld(astRoot[1]);

			InspectionTarget Tgt = BuidTgt(astRoot[2]);


			processRoot.Childes.Add(new ContainCheckProcess(processRoot, CP, Fld, Tgt));
			
		}

		private static InspectionTarget BuidTgt(KeyWordSearch.TreeParser.AstNode astNode)
		{
			string tmp = astNode.Value.TextValue.ToLower();

			if (tmp == "alpha")
			{
				return InspectionTarget.AlphaNumericNormalized;
			}
			else if (tmp == "kana")
			{
				return InspectionTarget.KanaNormalized;
			}
			else if (tmp == "both")
			{
				return InspectionTarget.BothNormalized;
			}
			else if (tmp == "nfc")
			{
				return InspectionTarget.NFC;
			}
			else
			{
				throw new RecognizationException("未知の正規化対象識別子です", astNode.Value);
			}
		}

		private static InspectionField BuildFld(parser::AstNode astNode)
		{
			switch (astNode.Value.Attribute)
			{
				case parser::TokenType.Identifyier:
					string tmp = astNode.Value.TextValue.ToLower();
					if (tmp == "title")
					{
						return InspectionField.Title;
					}
					else if (tmp == "description")
					{
						return InspectionField.Description;
					}
					else if (tmp == "station")
					{
						return InspectionField.StationName;
					}
					else if (tmp == "caster")
					{
						return InspectionField.CasterName;
					}
					else
					{
						throw new RecognizationException("未定義のフィールド識別子です", astNode.Value);
					}


				case parser::TokenType.OperatorOR:
					InspectionField fld = InspectionField.None;
					for (int i = 0; i < astNode.Count; i++)
					{
						fld |= BuildFld(astNode[i]);
					}
					return fld;

				default:
					throw new RecognizationException("未定義のフィールド識別子です", astNode.Value);
			}

			
		}

		private static string BuildCP(parser::AstNode astNode)
		{
			string tmp = astNode.Value.TextValue.Substring(1, astNode.Value.TextValue.Length - 2);
			tmp = tmp.Replace("\\\"", "\"");

			return tmp;
		}
	}
}
